#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <linux/types.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <unistd.h>

static int  spi_fd = -1;
static uint8_t  g_bright = 0xE0 + 0x01;

// An end frame consisting of at least (n/2) bits of 0, where n is the number of LEDs in the string.
// 最后还有n/2个0bit表示灯带上灯带个数
#define BUFFER_SIZE     (32 * 16 * 4 + 8 + 32)
static uint8_t  buffer[BUFFER_SIZE];

static PyObject * Open(PyObject *self, PyObject *args)
{
    uint32_t    max_speed_hz;
    uint8_t     mode;

    if (!PyArg_ParseTuple(args, "i", &max_speed_hz))
    {
        PyErr_SetString(PyExc_TypeError, "The max_speed_hz attribute must be an integer");
        return NULL;
    }

    spi_fd = open("/dev/spidev0.0", O_RDWR, 0);

    if (spi_fd == -1)
    {
        PyErr_SetFromErrno(PyExc_IOError);
        return NULL;
    }

    // Set mode
    if (ioctl(spi_fd, SPI_IOC_RD_MODE, &mode) == -1)
    {
        PyErr_SetFromErrno(PyExc_IOError);
        close(spi_fd);
        spi_fd = -1;
        return NULL;
    }

    mode = (0x0 & ~(SPI_CPHA | SPI_CPOL)) | mode;
    if (ioctl(spi_fd, SPI_IOC_WR_MODE, &mode) == -1)
    {
        close(spi_fd);
        spi_fd = -1;
        PyErr_SetFromErrno(PyExc_IOError);
        return NULL;
    }

    // Set max speed
    if (ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &max_speed_hz) == -1)
    {
        close(spi_fd);
        spi_fd = -1;
        PyErr_SetFromErrno(PyExc_IOError);
        return NULL;
	}

    memset(buffer, 0, sizeof(buffer));

    Py_RETURN_NONE;
}

static PyObject * SetGlobalBright(PyObject *self, PyObject *args)
{
    uint32_t    bright;
    
    if (PyArg_ParseTuple(args, "i", &bright))
    {
        if ((bright >= 0) && (bright <= 0x1F))
        {
            g_bright = 0xE0 + (uint8_t)bright;
            Py_RETURN_NONE;
        }
    }

    PyErr_SetString(PyExc_TypeError, "The bright must between 0~0x1F");
    return NULL;
}

static PyObject * Display(PyObject *self, PyObject *args)
{
    PyObject   *object = NULL;
    Py_buffer   view;

    if (!PyArg_ParseTuple(args, "O", &object))
    {
        return NULL;
    }

    if (PyObject_GetBuffer(object, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1)
    {
        return NULL;
    }

    if (view.ndim != 1)
    {
        printf("Expected a 1-dimensional array\r\n");
        PyBuffer_Release(&view); 
        return NULL;
    }

        /* Check the type of items in the array */
    if (strcmp(view.format, "B") != 0) 
    { 
        printf("Expected an array of byte"); 
        PyBuffer_Release(&view); 
        return NULL; 
    }

    uint8_t*    data = (uint8_t*)view.buf;

    int row, col;
    int s_pos, d_pos;

    buffer[0] = 0;
    buffer[1] = 0;
    buffer[2] = 0;
    buffer[3] = 0;

    buffer[BUFFER_SIZE - 1 - 32] = 0xff;
    buffer[BUFFER_SIZE - 2 - 32] = 0xff;
    buffer[BUFFER_SIZE - 3 - 32] = 0xff;
    buffer[BUFFER_SIZE - 4 - 32] = 0xff;
    // 奇数行
    for (row = 0; row < 16; row += 2)
    {
        for (col = 0; col < 32; col++)
        {
            s_pos = (row * 32 + col) * 3;
            d_pos = (row * 32 + (31 - col) + 1) * 4;
            buffer[d_pos] = g_bright;
            buffer[d_pos + 1] = data[s_pos + 2];
            buffer[d_pos + 2] = data[s_pos + 1];
            buffer[d_pos + 3] = data[s_pos];
        }
    }

    // 偶数行
    for (row = 1; row < 16; row += 2)
    {
        for (col = 0; col < 32; col++)
        {
            s_pos = (row * 32 + col) * 3;
            d_pos = (row * 32 + col + 1) * 4;
            buffer[d_pos] = g_bright;
            buffer[d_pos + 1] = data[s_pos + 2];
            buffer[d_pos + 2] = data[s_pos + 1];
            buffer[d_pos + 3] = data[s_pos];
        }
    }

    int status;

    status = write(spi_fd, buffer, BUFFER_SIZE);

	// 写入到SPI后，无论是否成功，都要释放内存
	PyBuffer_Release(&view); 

    if (status < 0)
    {
        PyErr_SetFromErrno(PyExc_IOError);
        return NULL;
    }

    Py_RETURN_NONE;
}

static PyMethodDef led_driverMethods[] = {
    {"Display",  Display, METH_VARARGS, "Send data to LED matrix."},
    {"Open", Open, METH_VARARGS, "Open SPI port."},
    {"SetGlobalBright", SetGlobalBright, METH_VARARGS, "Set global bright."},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

static struct PyModuleDef led_drivermodule = {
    PyModuleDef_HEAD_INIT,
    "led_driver",   /* name of module */
    NULL, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                 or -1 if the module keeps state in global variables. */
    led_driverMethods
};

PyMODINIT_FUNC
PyInit_led_driver(void)
{
    return PyModule_Create(&led_drivermodule);
}

